#include "pch.h"
#include "cTimeHelper.h"

#include <time.h>
#include <ctime>
#include <chrono>
#include <memory.h>
#include <sstream>
#include <iomanip>
#include <string>
#include <stdint.h>
#include <ctime>

namespace libtool_sdk
{
	namespace Helper
	{
		TOOL_SDK_API void  DLL_SPEC Tool_GetCurrentTimeToString(char* timeStrngBuf, int bufLen)
		{
			if(NULL == timeStrngBuf)
				return;
			std::time_t rawtime;
			struct tm * timeinfo;
			char buffer[256];

			std::time(&rawtime);								//get the current time
			timeinfo = std::localtime(&rawtime);		//convert time_t to struct*tm

            strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeinfo);		//format time to string

#ifdef _WIN32
			_snprintf(timeStrngBuf, bufLen, "%s", buffer);
#else
			snprintf(timeStrngBuf, bufLen, "%s", buffer);
#endif
			//return std::string(buffer);
		}

		TOOL_SDK_API unsigned long long DLL_SPEC Tool_ConvertToTimeTick(int iYear, int iMonth, int iDay, int iHour, int iMin, int iSecond, int iMsecond)
		{
			struct tm mytm;
			mytm.tm_year = iYear - 1900;
			mytm.tm_mon = iMonth - 1;
			mytm.tm_mday = iDay;
			mytm.tm_hour = iHour;
			mytm.tm_min = iMin;
			mytm.tm_sec = iSecond;

			time_t curTime = mktime(&mytm);

			unsigned long long  dw64Time = curTime;
			dw64Time = dw64Time * 1000 + iMsecond;

			return dw64Time;
		}

		TOOL_SDK_API long long DLL_SPEC Tool_GetTickCount()
		{
			using namespace std::chrono;

			system_clock::time_point now = system_clock::now();

			auto duration = now.time_since_epoch();
			long long  iMillisecond = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

			return iMillisecond;
		}

		TOOL_SDK_API long long DLL_SPEC Tool_GetTimeSecond()
		{
			using namespace std::chrono;

			system_clock::time_point now = system_clock::now();

			auto duration = now.time_since_epoch();
			int64_t iSecond = std::chrono::duration_cast<std::chrono::seconds>(duration).count();

			return iSecond;
		}

		TOOL_SDK_API void DLL_SPEC Tool_GetDateTimeWithMillisecond(DateTimeWithMilsec* timeStruct)
		{
			if (NULL == timeStruct)
			{
				printf("timeStruct is NULL");
				return;
			}
                
			auto now = std::chrono::system_clock::now();
			time_t in_time_t = std::chrono::system_clock::to_time_t(now);
			struct tm* ptmNow = localtime(&in_time_t);

			auto duration = now.time_since_epoch();
			long long  iMillisecond = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

			timeStruct->year = ptmNow->tm_year + 1900;
			timeStruct->month = ptmNow->tm_mon + 1;
			timeStruct->day = ptmNow->tm_mday;
			timeStruct->hour = ptmNow->tm_hour;
			timeStruct->minute = ptmNow->tm_min;
			timeStruct->second = ptmNow->tm_sec;
			timeStruct->millisecond = static_cast<int>(iMillisecond % 1000);
		}

		TOOL_SDK_API tm DLL_SPEC Tool_TimeNowTm()
		{
			time_t now = std::time(0);
			return *std::localtime(&now);
		}

		TOOL_SDK_API bool  DLL_SPEC Tool_GetTimeString(char* timeStrngBuf, int bufLen)
		{
			if (NULL == timeStrngBuf)
			{
				printf("timeStrngBuf is NULL");
                return false;
			}

			DateTimeWithMilsec timeStruct;
			Tool_GetDateTimeWithMillisecond(&timeStruct);

			char chTime[64] = {0};
			sprintf(chTime, "%04d%02d%02d%02d%02d%02d%03d",
				timeStruct.year,
				timeStruct.month,
				timeStruct.day,
				timeStruct.hour,
				timeStruct.minute,
				timeStruct.second,
				timeStruct.millisecond);

#ifdef _WIN32
			_snprintf(timeStrngBuf, bufLen, "%s", chTime);
#else
			snprintf(timeStrngBuf, bufLen, "%s", chTime);
#endif
			return true;
		}

		TOOL_SDK_API bool DLL_SPEC Tool_ConverTimeTickWithMsToString(unsigned long long timeTick, char* buff, int bufLen)
		{
			if (0 != timeTick)
			{
				//CTime tm(CamResult->dw64TimeMS / 1000);
				struct tm *timeNow = NULL;
				time_t          timeSecond = timeTick / 1000;  // Seconds
				timeNow = localtime(&timeSecond);

				if (bufLen >= 17)
				{
					memset(buff, '\0', bufLen);
					sprintf(buff, "%04d%02d%02d%02d%02d%02d%03llu",
						timeNow->tm_year + 1900,
						timeNow->tm_mon + 1,
						timeNow->tm_mday,
						timeNow->tm_hour,
						timeNow->tm_min,
						timeNow->tm_sec,
						timeTick % 1000);

					return true;
				}
				return false;
			}
			return false;
		}

		TOOL_SDK_API unsigned long long DLL_SPEC Tool_ConvertTimeStringToTick64(const char* timeString)
		{
			if (timeString == NULL || strlen(timeString) < 18 || strlen(timeString) > 23)
			{
				return 0;
			}
			size_t iStrLen = strlen(timeString);

			int iYear = 0, iMonth = 0, iDay = 0, iHour = 0, iMin = 0, iSec = 0, iMsec = 0;
			std::string strTimeFormat = (iStrLen < 20) ? "%d-%d-%d %d:%d:%d" : "%d-%d-%d %d:%d:%d:%d";
			int iCount = sscanf(timeString, strTimeFormat.c_str(),
				&iYear,
				&iMonth,
				&iDay,
				&iHour,
				&iMin,
				&iSec,
				&iMsec
			);

			struct tm timeStruc;
			timeStruc.tm_year = iYear - 1900;
			timeStruc.tm_mon = iMonth - 1;
			timeStruc.tm_mday = iDay;
			timeStruc.tm_hour = iHour;
			timeStruc.tm_min = iMin;
			timeStruc.tm_sec = iSec;

			std::time_t timTick = std::mktime(&timeStruc);

			unsigned long long dw64TimeTick = (unsigned long long)timTick * 1000 + iMsec;
			return dw64TimeTick;
		}

		TOOL_SDK_API bool DLL_SPEC Tool_CheckIfDateTimeIsValid(int iYear, int iMonth, int iDay, int iHour, int iMinute, int iSecond)
		{
			const int MAX_VALID_YR = 9999;
			const int MIN_VALID_YR = 1800;
			auto func_isLeap = [](int year)
			{
				// Return true if year
				// is a multiple pf 4 and
				// not multiple of 100.
				// OR year is multiple of 400.
				return (((year % 4 == 0) &&
					(year % 100 != 0)) ||
					(year % 400 == 0));
			};

			// If year, month and day
			// are not in given range
			if (iYear > MAX_VALID_YR
				|| iYear < MIN_VALID_YR)
			{
				return false;
			}

			if (iMonth < 1 || iMonth > 12)
				return false;


			if (iDay < 1 || iDay > 31)
				return false;

			// Handle February month
			// with leap year
			if (iMonth == 2)
			{
				if (func_isLeap(iYear))
					return (iDay <= 29);
				else
					return (iDay <= 28);
			}

			// Months of April, June,
			// Sept and Nov must have
			// number of days less than
			// or equal to 30.
			if (iMonth == 4
				|| iMonth == 6
				|| iMonth == 9
				|| iMonth == 11
				)
			{
				return (iDay <= 30);
			}

			if (iHour >= 0
				&& iHour <= 24
				&& iMinute >= 0
				&& iMinute <= 59
				&& iSecond >= 0
				&& iSecond <= 59)
			{
				return true;
			}
			else
			{
				return false;
			}
		}

		TOOL_SDK_API long long DLL_SPEC Tool_ConvertTimeStringToTimeTick(const char* timeString, const char* timeFormat)
		{
			if (NULL == timeString || NULL == timeFormat)
			{
				return 0;
			}
			// Let's consider we are getting all the input in
			// this format: '2014-07-25T20:17:22Z' (T denotes
			// start of Time part, Z denotes UTC zone).
			// A better approach would be to pass in the format as well.
			//static const std::string dateTimeFormat{"%Y-%m-%dT%H:%M:%SZ" };
			//static const std::string dateTimeFormat{ "%Y/%m/%d %H:%M:%S" };

			// Create a stream which we will use to parse the string,
			// which we provide to constructor of stream to fill the buffer.
			std::istringstream ss{ timeString };

			// Create a tm object to store the parsed date and time.
			std::tm dt = { 0,0,0,0,0,0,0,0,0 };

			// Now we read from buffer using get_time manipulator
			// and formatting the input appropriately.
			ss >> std::get_time(&dt, timeFormat);

			// Convert the tm structure to time_t value and return.
			return std::mktime(&dt);
		}

		TOOL_SDK_API long long DLL_SPEC Tool_ConvertTimeStringToTimeTick_sscanfversion(const char* timeString, const char* timeFormat)
        {
            if (NULL == timeString || NULL == timeFormat)
            {
                return 0;
            }

            int iYear = 0, iMonth = 0, iDay =  0,iHour= 0, iMin=0, iSec =0;
            sscanf(timeString, timeFormat,
                   &iYear,
                   &iMonth,
                   &iDay,
                   &iHour,
                   &iMin,
                   &iSec
                   );

            if (    iYear <= 1900
                    || iMonth < 1 || iMonth > 12
                    || iHour < 0 || iHour > 23
                    || iMin < 0 || iMin > 59
                    || iSec < 0 || iSec > 59)
            {
                return 0;
            }

            if ((((iYear % 4 == 0 && iYear % 100 != 0) || (iYear % 400 == 0)) && iDay > 29 && iMonth == 2)
                || (!((iYear % 4 == 0 && iYear % 100 != 0) || (iYear % 400 == 0)) && iDay > 28 && iMonth == 2)
                || ((iMonth == 1 || iMonth == 3 || iMonth == 5 || iMonth == 7 || iMonth == 8 || iMonth == 10 || iMonth == 12) && iDay > 31)
                || (!(iMonth == 1 || iMonth == 3 || iMonth == 5 || iMonth == 7 || iMonth == 8 || iMonth == 10 || iMonth == 12) && iDay > 30)
                || iDay < 1)
            {
                return 0;
            }

            struct tm timeStruc;
            timeStruc.tm_year = iYear-1900;
            timeStruc.tm_mon = iMonth-1;
            timeStruc.tm_mday = iDay;
            timeStruc.tm_hour = iHour;
            timeStruc.tm_min = iMin;
            timeStruc.tm_sec = iSec;


            std::time_t timTick = std::mktime(&timeStruc);
            return timTick;
        }

	}
}
